package top.wshape1.shiziapi.uaa.security;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.util.Assert;
import top.wshape1.shiziapi.common.constant.RedisConstant;
import top.wshape1.shiziapi.uaa.json.deserializer.OAuth2AuthorizationRequestJsonDeserializer;
import top.wshape1.shiziapi.uaa.json.serializer.OAuth2AuthorizationRequestJsonSerializer;

import java.util.concurrent.TimeUnit;

/**
 * @author Wshape1
 * @version 1.0
 * @description OAuth2AuthorizationRequest存储在Redis中
 * @since 2023-08-30
 */

public class RedisOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {

    private final RedisTemplate<String, Object> redisTemplate;

    private final String REDIS_KEY_PREFIX = RedisConstant.OAUTH2_LOGIN_ID_KEY;

    private final ObjectMapper objectMapper;

    public RedisOAuth2AuthorizationRequestRepository(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.objectMapper = oAuth2AuthorizationRequestObjectMapper();
    }

    @Override
    public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
        Assert.notNull(request, "request cannot be null");
        String stateParameter = getStateParameter(request);
        if (stateParameter == null) {
            return null;
        }
        OAuth2AuthorizationRequest authorizationRequest = getAuthorizationRequest(request);
        return (authorizationRequest != null && stateParameter.equals(authorizationRequest.getState()))
                ? authorizationRequest : null;
    }

    @Override
    public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, HttpServletResponse response) {
        Assert.notNull(request, "request cannot be null");
        Assert.notNull(response, "response cannot be null");
        if (authorizationRequest == null) {
            removeAuthorizationRequest(request, response);
            return;
        }
        String state = authorizationRequest.getState();
        Assert.hasText(state, "authorizationRequest.state cannot be empty");
        try {
            redisTemplate.opsForValue().set(REDIS_KEY_PREFIX + state, objectMapper.writeValueAsString(authorizationRequest), 10, TimeUnit.MINUTES);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request, HttpServletResponse response) {
        Assert.notNull(response, "response cannot be null");
        OAuth2AuthorizationRequest authorizationRequest = loadAuthorizationRequest(request);
        if (authorizationRequest != null) {
            redisTemplate.delete(REDIS_KEY_PREFIX + authorizationRequest.getState());
        }
        return authorizationRequest;
    }

    private String getStateParameter(HttpServletRequest request) {
        return request.getParameter(OAuth2ParameterNames.STATE);
    }

    private OAuth2AuthorizationRequest getAuthorizationRequest(HttpServletRequest request) {
        String oAuth2LoginId = getStateParameter(request);
        try {
            Object o = redisTemplate.opsForValue().get(REDIS_KEY_PREFIX + oAuth2LoginId);
            return (oAuth2LoginId != null && o != null) ?
                    objectMapper.readValue(
                            (String) o,
                            OAuth2AuthorizationRequest.class) : null;
        } catch (JsonProcessingException e) {
            return null;
        }
    }

    public ObjectMapper oAuth2AuthorizationRequestObjectMapper() {
        ObjectMapper om = new ObjectMapper();
        // 为OAuth2AuthorizationRequest专门定制序列化器
        om.registerModule(new SimpleModule()
                .addSerializer(OAuth2AuthorizationRequest.class, new OAuth2AuthorizationRequestJsonSerializer())
                .addDeserializer(OAuth2AuthorizationRequest.class, new OAuth2AuthorizationRequestJsonDeserializer())
        );
        return om;
    }

}
